/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package oa.game;

import java.io.*;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author gmarcone
 */
public class Loader implements Closeable {

    private static final Logger logger = Logger.getLogger(Loader.class.getName());
    private static final String LOG_LOCATION = "/home/gmarcone/Descargas/openarena_logs";
    private static final SimpleDateFormat fechaJuegoFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static final Pattern patternInitGame = Pattern.compile("(.*?) InitGame: .*");
    private static final Pattern patternKill = Pattern.compile(".*?Kill: .*?: (.*?) killed (.*?) by (.*?)");
    private static final Pattern patternPlayerScore = Pattern.compile("(.*?) PlayerScore: .*?: (.*?) now has (.*?) points");
    private static final Pattern patternShutdownGame = Pattern.compile("(.*?) ShutdownGame:.*");
    private static final Pattern patternFlagGot = Pattern.compile("(.*?)CTF: .*?: (.*?) got the (RED|BLUE) flag!");
    private static final Pattern patternFlagCapture = Pattern.compile("(.*?)CTF: .*?: (.*?) captured the (RED|BLUE) flag!");
    private static final Pattern patternFlagCarrierFragged = Pattern.compile("(.*?)CTF: .*?: (.*?) fragged (RED|BLUE)'s flag carrier!");
    private static final Pattern patternFlagReturned = Pattern.compile("(.*?)CTF: .*?: (.*?) returned the (RED|BLUE) flag!");
    private static final Pattern patternCaptureLimit = Pattern.compile("(.*?) Exit: Capturelimit hit.*");
    private static final Pattern patternTimeLimit = Pattern.compile("(.*?) Exit: Timelimit hit.*");

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Loader l = null;
        try {
            l = new Loader();
            new Loader().cargarJuegos(LOG_LOCATION);
        } catch (Exception e) {
            logger.log(Level.SEVERE, null, e);
        } finally {
            MyUtils.silentClose(l);
        }
    }
    private DBUtils dbUtils;

    public Loader() throws SQLException {
        this.dbUtils = new DBUtils();

    }

    private void cargarJuegos(String logLocation) throws IOException {
        File dir = new File(logLocation);

        if (dir.isDirectory()) {

            File[] files = dir.listFiles();

            for (File file : files) {
                if (file.isFile()) {
                    processLog(file);
                }
            }

        } else {
            logger.log(Level.SEVERE, "{0} is not a directory", logLocation);
        }
    }

    private void processLog(File logFile) throws IOException {

        BufferedReader leer = new BufferedReader(new InputStreamReader(new FileInputStream(logFile)));
        String linea;
        Matcher m;
        try {
            while ((linea = leer.readLine()) != null) {
                m = patternInitGame.matcher(linea);
                if (m.matches()) {
                    Map<String, String> parametrosJuego = getParametrosJuego(linea);

                    try {
                        if (!dbUtils.existeJuego(parametrosJuego)) {
                            cargarJuego(parametrosJuego, linea, m.group(1), leer);
                        }
                    } catch (Exception e) {
                        logger.log(Level.SEVERE, "Could not create game", e);
                    }
                }
            }
        } finally {
            MyUtils.silentClose(leer);
        }
    }

    private static Map<String, String> getParametrosJuego(String lineaInicial) {

        if (lineaInicial == null || lineaInicial.length() == 0) {
            throw new NullPointerException("lineaInicial can not be null or empty");
        }

        lineaInicial = lineaInicial.substring(lineaInicial.indexOf("\\") + 1);
        Map<String, String> parametrosJuego = new HashMap<String, String>();

        String[] split = lineaInicial.split("\\\\");
        for (int i = 0; i < split.length / 2; i++) {
            parametrosJuego.put(split[2 * i], split[2 * i + 1]);
        }

        return parametrosJuego;
    }

    private boolean procesarLinea(Juego juego, BufferedReader leer) throws IOException, SQLException, InfArenaDataMissingException {

        String linea = leer.readLine();

        if (linea == null) {
            return true;
        }

        Matcher m;

        m = patternKill.matcher(linea);
        if (m.matches()) {
            cargarMuerte(juego, m.group(1), m.group(2), m.group(3));
            return false;
        }

        m = patternPlayerScore.matcher(linea);
        if (m.matches()) {
            this.nuevosPuntos(juego, m.group(1), m.group(2), m.group(3));
            return false;
        }

        m = patternShutdownGame.matcher(linea);
        if (m.matches()) {
            return true;
        }

        m = patternFlagGot.matcher(linea);
        if (m.matches()) {
            this.nuevoAccionBandera(juego, m.group(2), AccionesBandera.GOT_FLAG, m.group(1), linea);
            return false;
        }

        m = patternFlagCapture.matcher(linea);
        if (m.matches()) {
            this.nuevoAccionBandera(juego, m.group(2), AccionesBandera.CAPTURED_FLAG, m.group(1), linea);
            return false;
        }

        m = patternFlagCarrierFragged.matcher(linea);
        if (m.matches()) {
            this.nuevoAccionBandera(juego, m.group(2), AccionesBandera.FRAGGED_CARRIER, m.group(1), linea);
            return false;
        }

        m = patternFlagReturned.matcher(linea);
        if (m.matches()) {
            this.nuevoAccionBandera(juego, m.group(2), AccionesBandera.RETURNED_FLAG, m.group(1), linea);
            return false;
        }

        m = patternCaptureLimit.matcher(linea);
        if (m.matches()) {
            finalizarJuego(juego, m.group(1));
            setGanadores(juego, leer.readLine());

            return true;
        }

        m = patternTimeLimit.matcher(linea);
        if (m.matches()) {
            finalizarJuego(juego, m.group(1));
            setGanadores(juego, leer.readLine());

            return true;
        }

        return false;
    }

    private void cargarJuego(Map<String, String> parametrosJuego, String lineaInicial, String stringtiempoInicio, BufferedReader leer) throws IOException, SQLException {
        String fechaJuego = parametrosJuego.get("g_timestamp");

        long gameId = 0;
        try {
            gameId = fechaJuegoFormat.parse(fechaJuego).getTime();
        } catch (ParseException ex) {
            logger.log(Level.SEVERE, null, ex);
        }

        boolean fin = (gameId == 0);

        Juego juego = new Juego(this.dbUtils);
        juego.setTiempoInicio(toSeconds(stringtiempoInicio));
        juego.setParametros(parametrosJuego);

        while (!fin) {
            try {
                fin = procesarLinea(juego, leer);
            } catch (InfArenaDataMissingException ex) {
                fin = true;
                juego.setValido(false);
                logger.log(Level.SEVERE, null, ex);
            }
        }

        juego.salvarJuego();
    }

    protected static int toSeconds(String tiempo) {
        int tiempoSegundos = 0;

        tiempo = tiempo.trim();

        String[] split = tiempo.split(":");

        try {
            for (String string : split) {
                tiempoSegundos = tiempoSegundos * 60 + Integer.valueOf(string);
            }
        } catch (NumberFormatException e) {
            logger.log(Level.SEVERE, null, e);
        }

        return tiempoSegundos;
    }

    private void cargarMuerte(Juego juego, String apodoKiller, String apodoVictim, String apodoArma) throws SQLException, InfArenaDataMissingException {

        if (ignorarMuerte(apodoKiller, apodoVictim, apodoArma)) {
            return;
        }
        int idKiller = dbUtils.getIdJugador(apodoKiller);
        int idVictim = dbUtils.getIdJugador(apodoVictim);
        int idArma = dbUtils.getIdArma(apodoArma);

        if (idKiller < 0) {
            throw new InfArenaDataMissingException("Jugador desconocido " + apodoKiller);
        }
        if (idVictim < 0) {
            throw new InfArenaDataMissingException("Jugador desconocido " + apodoVictim);
        }
        if (idArma < 0) {
            throw new InfArenaDataMissingException("Arma desconocida " + apodoArma);
        }

        juego.cargarMuerte(idKiller, idVictim, idArma);
    }

    @Override
    public void close() throws IOException {
        MyUtils.silentClose(dbUtils);
    }

    private void nuevosPuntos(Juego juego, String time, String playerNick, String puntos) throws SQLException, InfArenaDataMissingException {
        int idPlayer = dbUtils.getIdJugador(playerNick);
        if (idPlayer < 0) {
            throw new InfArenaDataMissingException("Jugador desconocido " + playerNick);
        }

        int secondsTime = toSeconds(time);
        int intPuntos = Integer.parseInt(puntos);

        juego.addPuntosJugador(idPlayer, intPuntos, secondsTime);
    }

    private void nuevoAccionBandera(Juego juego, String njugador, AccionesBandera accion, String tiempo, String linea) throws SQLException, InfArenaDataMissingException {
        int idPlayer = dbUtils.getIdJugador(njugador);
        if (idPlayer < 0) {
            throw new InfArenaDataMissingException("Jugador desconocido " + njugador);
        }

        int segundos = toSeconds(tiempo);
        juego.cargarAccionBandera(idPlayer, accion, segundos, linea);
    }

    private void finalizarJuego(Juego juego, String tiempoFin) {
        int secondFin = toSeconds(tiempoFin);

        juego.setTiempoFin(secondFin);

        juego.setValido(true);
    }

    public void setGanadores(Juego juego, String line) {
        String[] split = line.split(" ");

        int countRed = 0;
        int countBlue = 0;
        for (String string : split) {
            if (string.contains("RED".toLowerCase())) {
                String[] split2 = string.split(":");
                countRed = Integer.parseInt(split2[1]);
            } else if (string.contains("BLUE".toLowerCase())) {
                String[] split2 = string.split(":");
                countBlue = Integer.parseInt(split2[1]);
            }
        }

        if (countBlue > countRed) {
            juego.setGanadores("BLUE");
        } else {
            juego.setGanadores("RED");
        }
    }

    private boolean ignorarMuerte(String apodoKiller, String apodoVictim, String apodoArma) {
        if (apodoKiller.equals(apodoVictim)) {
            return true;
        } else if (apodoKiller.equals("<world>")) {
            return true;
        } else if (apodoVictim.equals("<world>")) {
            return true;
        } else if (apodoArma.equals("MOD_SUICIDE")) {
            return true;
        } else if (apodoArma.equals("MOD_FALLING")) {
            return true;
        }

        return false;
    }
}
